<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 6.0.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"samstring.github.io.git","root":"/","scheme":"Pisces","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
  </script>

  <meta name="description" content="一 前言说明本文主要讲述的是flutter的构建流程。一步步地去分析Flutter是如何从上往下构建Widget，顺便带出Flutter日常开发中的基本概念。 源码警告⚠️ 本文在分析过程中会夹带部分源码，整体边幅较长。友情提示，通过导航栏去到相应的地方 问题希望你能在阅读的过程能带着以下的一些问题。  Widget,Element,RenderObject是什么，三者的关系是怎样的? build">
<meta property="og:type" content="article">
<meta property="og:title" content="Flutter源码阅读(1)-Widget,Element,RenderObject树的构建和更新流程">
<meta property="og:url" content="https://samstring.github.io.git/2022/01/08/Flutter/1/flutter1/index.html">
<meta property="og:site_name" content="samstring">
<meta property="og:description" content="一 前言说明本文主要讲述的是flutter的构建流程。一步步地去分析Flutter是如何从上往下构建Widget，顺便带出Flutter日常开发中的基本概念。 源码警告⚠️ 本文在分析过程中会夹带部分源码，整体边幅较长。友情提示，通过导航栏去到相应的地方 问题希望你能在阅读的过程能带着以下的一些问题。  Widget,Element,RenderObject是什么，三者的关系是怎样的? build">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b45ec5b603a04543b6fbfc95c2b6efe9~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4fe23dd692c049a4b09a2a2e8a0a219b~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b60e29d343074042a953d14cf6ae70f9~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fcaed2121e104af392778a497d8ad083~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/578032ec139842d0b90a39a3038f0dd8~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ae694ca8696542779a7afb1abfcaef8d~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9d4a3b12d51b4bcca1589bcd008e1dd8~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6beea744b12b4020acea213eaedec072~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bc0da1b3b35041e79ff74821225c7936~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bd58ffb979cc442ea03af1bb1ef154ab~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3bbb765087784e8b901e107db6e88f6c~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e0430efdd94841cb8990c84604d3ce7f~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9368448436404d8db6f2609bbde7b4b9~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b6e8ad281ae943de8892b37c7b3c7510~tplv-k3u1fbpfcp-watermark.image">
<meta property="og:image" content="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/53c905ca43b342c2a480e26ce2d04f2a~tplv-k3u1fbpfcp-watermark.image">
<meta property="article:published_time" content="2022-01-07T16:00:38.000Z">
<meta property="article:modified_time" content="2022-01-08T07:38:58.486Z">
<meta property="article:author" content="samstring">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b45ec5b603a04543b6fbfc95c2b6efe9~tplv-k3u1fbpfcp-watermark.image">

<link rel="canonical" href="https://samstring.github.io.git/2022/01/08/Flutter/1/flutter1/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'en'
  };
</script>

  <title>Flutter源码阅读(1)-Widget,Element,RenderObject树的构建和更新流程 | samstring</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Toggle navigation bar">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">samstring</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">一个平平无奇的程序猿</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-主页">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>主页</a>

  </li>
        <li class="menu-item menu-item-分类">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-归档">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
    <link itemprop="mainEntityOfPage" href="https://samstring.github.io.git/2022/01/08/Flutter/1/flutter1/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://p9-passport.byteacctimg.com/img/user-avatar/b02abbd29495eadaaf476f3b5cb129bf~300x300.image">
      <meta itemprop="name" content="samstring">
      <meta itemprop="description" content="问号青年">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="samstring">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          Flutter源码阅读(1)-Widget,Element,RenderObject树的构建和更新流程
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>
              

              <time title="Created: 2022-01-08 00:00:38 / Modified: 15:38:58" itemprop="dateCreated datePublished" datetime="2022-01-08T00:00:38+08:00">2022-01-08</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">In</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Flutter/" itemprop="url" rel="index"><span itemprop="name">Flutter</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h2 id="一-前言"><a href="#一-前言" class="headerlink" title="一 前言"></a>一 前言</h2><h3 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h3><p>本文主要讲述的是flutter的构建流程。一步步地去分析Flutter是如何从上往下构建Widget，顺便带出Flutter日常开发中的基本概念。</p>
<p>源码警告⚠️ 本文在分析过程中会夹带部分源码，整体边幅较长。友情提示，通过导航栏去到相应的地方</p>
<h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><p>希望你能在阅读的过程能带着以下的一些问题。</p>
<ol>
<li>Widget,Element,RenderObject是什么，三者的关系是怎样的?</li>
<li>build方法中的BuildContext是什么？</li>
<li>setState发生了什么?</li>
<li>为什么GlobalKey可以使你在任意地方获取Widget?</li>
</ol>
<span id="more"></span>
<hr>
<p>正文分割线</p>
<hr>
<h2 id="二-Widget-Element-RenderObjct"><a href="#二-Widget-Element-RenderObjct" class="headerlink" title="二 Widget,Element,RenderObjct"></a>二 Widget,Element,RenderObjct</h2><h3 id="Widget-Element-RenderObject说明"><a href="#Widget-Element-RenderObject说明" class="headerlink" title="Widget,Element,RenderObject说明"></a>Widget,Element,RenderObject说明</h3><p>在日常开发中，我们经常接触到都是Widget,包括各种的StalessWidget,StafulWidget,仿佛各种Widget就是界面上的一个节点。但是，Widget并不是界面上的一个节点.它只是一个描述节点的工具。请参照以下案例</p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b45ec5b603a04543b6fbfc95c2b6efe9~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<p>其实按照我们的开发经验可知。一个节点是无法同时出现在两个地方。就像原生开发中。一个按钮是无法同时添加到两个地方的。Widget就是一个描述文件，负责产生一个节点，以及说明这个节点的功能是什么。</p>
<p>那现在问题来了，到底什么是节点呢？Flutter中的节点是一个比较神奇的存在。它不仅可用于表达UI界面上的一个元素，也可以用来表达一种布局信息，或是存储依赖关系等。</p>
<p>有一种说法是flutter中万物皆是Widget，其内在的意义就是flutter中的节点可以表示开发中的大部分的元素。flutter中的节点是用Element来表示。</p>
<p>既然说到节点Element可以表达大部分元素。那么具体到要产生在我们能看到UI信息，是怎么做到的呢？</p>
<p>首先我们先看看Widget类的定义</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Widget</span> <span class="keyword">extends</span> <span class="title">DiagnosticableTree</span> </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> Widget(&#123; <span class="keyword">this</span>.key &#125;);</span><br><span class="line">  <span class="keyword">final</span> Key? key;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">//创建节点Element</span></span><br><span class="line">  <span class="meta">@protected</span></span><br><span class="line">  <span class="meta">@factory</span></span><br><span class="line">  <span class="built_in">Element</span> createElement();</span><br><span class="line"></span><br><span class="line"> ...</span><br><span class="line">  <span class="keyword">static</span> <span class="built_in">bool</span> canUpdate(Widget oldWidget, Widget newWidget) &#123;</span><br><span class="line">    <span class="keyword">return</span> oldWidget.runtimeType == newWidget.runtimeType</span><br><span class="line">        &amp;&amp; oldWidget.key == newWidget.key;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Widget类是一个抽象类，其中有一个createElement()方法。这个方法是用来创建节点Element。至于创建什么类型的Element。具体由Widget子类去实现</p>
<p>然后我们先看下Widget和Element这两个类的继承树</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4fe23dd692c049a4b09a2a2e8a0a219b~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b60e29d343074042a953d14cf6ae70f9~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<p>从图片可以很容易地观察出，每一种类型的Widget都有对应的Element。RenderObjectWidget是用来构建具体显示在界面上的节点的描述信息类。在这里我们重点看下RenderObjectWidget和RenderObjectElement。</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">RenderObjectWidget</span> <span class="keyword">extends</span> <span class="title">Widget</span> </span>&#123;</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">const</span> RenderObjectWidget(&#123; Key? key &#125;) : <span class="keyword">super</span>(key: key);</span><br><span class="line">  <span class="comment">//创建RenderObjectElement类型的节点</span></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="meta">@factory</span></span><br><span class="line">  RenderObjectElement createElement();</span><br><span class="line">  <span class="comment">//创建RenderObject</span></span><br><span class="line">  <span class="meta">@protected</span></span><br><span class="line">  <span class="meta">@factory</span></span><br><span class="line">  RenderObject createRenderObject(BuildContext context);</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line">  <span class="meta">@protected</span></span><br><span class="line">  <span class="keyword">void</span> updateRenderObject(BuildContext context, <span class="keyword">covariant</span> RenderObject renderObject) &#123; &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@protected</span></span><br><span class="line">  <span class="keyword">void</span> didUnmountRenderObject(<span class="keyword">covariant</span> RenderObject renderObject) &#123; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在这里，我们可以看到RenderObjectWidget的createElement()方法会创建RenderObjectElement一个RenderObjectElement。然后看下RenderObjectElement的代码</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">RenderObjectElement</span> <span class="keyword">extends</span> <span class="title">Element</span> </span>&#123;</span><br><span class="line">  RenderObjectElement(RenderObjectWidget widget) : <span class="keyword">super</span>(widget);</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  RenderObjectWidget <span class="keyword">get</span> widget =&gt; <span class="keyword">super</span>.widget <span class="keyword">as</span> RenderObjectWidget;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  RenderObject <span class="keyword">get</span> renderObject =&gt; _renderObject!;</span><br><span class="line">  RenderObject? _renderObject;</span><br><span class="line">  <span class="comment">//mount方法极其重要，是所有Elemnt子类都必须需要实现方法</span></span><br><span class="line">  <span class="comment">//就是通过这个方法一层层地往下构建Element节点树</span></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="keyword">void</span> mount(<span class="built_in">Element?</span> parent, <span class="built_in">Object?</span> newSlot) &#123;</span><br><span class="line">    <span class="keyword">super</span>.mount(parent, newSlot);</span><br><span class="line">   ...</span><br><span class="line">     #</span><br><span class="line">     _renderObject = widget.createRenderObject(<span class="keyword">this</span>);</span><br><span class="line">   ...</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="keyword">void</span> update(<span class="keyword">covariant</span> RenderObjectWidget newWidget) &#123;</span><br><span class="line">    <span class="keyword">super</span>.update(newWidget);</span><br><span class="line">    ...</span><br><span class="line">    _dirty = <span class="keyword">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>结合RenderObjectWidget和RenderObjectElement的源码，我们可以看到这么一个基础的流程</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fcaed2121e104af392778a497d8ad083~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<p>可以看到Widget,Element和RenerObject三者的关系如下</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/578032ec139842d0b90a39a3038f0dd8~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<p>值得一提的是，RenderObjct其实还不是真正显示在界面上的元素，其背后还有一个用于绘制的图层layer。关系有点像iOS开发中的UIView和CALayer。</p>
<p>这里小结一下</p>
<ul>
<li>Element是Flutter中代表一个节点的类。这里的节点不单单指UI上的界面元素</li>
<li>Widget是Element的描述类，用于说明创建一个怎样的Element节点，</li>
<li>RenderObjct是用于表示显示在UI上的布局和元素</li>
</ul>
<hr>
<h2 id="三-Widget树构建"><a href="#三-Widget树构建" class="headerlink" title="三 Widget树构建"></a>三 Widget树构建</h2><h3 id="Widget树是什么？"><a href="#Widget树是什么？" class="headerlink" title="Widget树是什么？"></a>Widget树是什么？</h3><p>我们平常的学习过程中，经常会遇到Widget,Element和RenderObjct三棵树的概念，那到底这是什么意思呢，三棵树到底又是如果构建呢？结合前面三者的关系，我们接着分析一下。首先我们先看下具体的demo。如下</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ae694ca8696542779a7afb1abfcaef8d~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<p>图中左方是demo的效果，中间是demo的代码，右方是demo中的各个Widget。可以看到，右方的所有Widget以一个树状的方式层级展开。形象一点的话，如下</p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9d4a3b12d51b4bcca1589bcd008e1dd8~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<p>可以看到，Widget以一个树状的方式去一级级往下走。我们在写Flutter项目的时候，所有的Widget都会以这样的一个树状层级构建，这个就是Widget树的概念。</p>
<h3 id="Widget树怎么构建？"><a href="#Widget树怎么构建？" class="headerlink" title="Widget树怎么构建？"></a>Widget树怎么构建？</h3><p>首先我们从main函数开发分析，我提炼一下重点，main函数执行后的流程如下</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6beea744b12b4020acea213eaedec072~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<p>main函数最终会执行RenderObjectToWidgetAdapter类的attachToRenderTree方法。attachToRenderTree方法如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">RenderObjectToWidgetElement&lt;T&gt; attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement&lt;T&gt;? element ]) &#123;</span><br><span class="line">   <span class="keyword">if</span> (element == <span class="keyword">null</span>) &#123;</span><br><span class="line">     owner.lockState(() &#123;</span><br><span class="line">       <span class="comment">//创建RenderObjectToWidgetElement节点</span></span><br><span class="line">       element = createElement();</span><br><span class="line">       <span class="keyword">assert</span>(element != <span class="keyword">null</span>);</span><br><span class="line">       <span class="comment">//为节点设置一个BuildOwner</span></span><br><span class="line">       element!.assignOwner(owner);</span><br><span class="line">     &#125;);</span><br><span class="line">     <span class="comment">//调用BuildOwner的buildScope方法</span></span><br><span class="line">     owner.buildScope(element!, () &#123;</span><br><span class="line">       element!.mount(<span class="keyword">null</span>, <span class="keyword">null</span>);</span><br><span class="line">     &#125;);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">     element._newWidget = <span class="keyword">this</span>;</span><br><span class="line">     element.markNeedsBuild();</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> element!;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>在构建过程中，这个方法做了两个事情，构建RenderObjectToWidgetElement节点，并把节点和回调传入BuildOwner的buildScope方法。这个其实就是构建我们Widget树的开端。</p>
<h4 id="构建管理者BuilderOwner"><a href="#构建管理者BuilderOwner" class="headerlink" title="构建管理者BuilderOwner"></a>构建管理者BuilderOwner</h4><p>你可以理解为BuilderOwner为一个Widget树的管理者，负责Widget树的管理和更新。他与Widget，Element和RenderObjct的关系大致如下</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bc0da1b3b35041e79ff74821225c7936~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<p>BuilderOwner不与Widget直接接触，而是通过构建，更新Element树，从而构建和更新Widget树。</p>
<p>除了构建和更新Widget和Element树，BuilderOwner还负责管理GlobalKey等操作</p>
<h4 id="构建流程"><a href="#构建流程" class="headerlink" title="构建流程"></a>构建流程</h4><p>从前面我们看到runApp()方法会调用到RenderObjectToWidgetAdapter的attachToRenderTree方法中。这个方法RenderObjectToWidgetElement节点，并把节点和回调传入BuildOwner的buildScope方法。BuildOwner类和buildScope方法因为和更新过程联系比较大，稍后再详细描述，这里需要知道的是，buildScope会执行回调，也就是下面的代码</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//调用BuildOwner的buildScope方法</span></span><br><span class="line"><span class="comment">//根据上方代码可知，element是RenderObjectToWidgetElement</span></span><br><span class="line">owner.buildScope(element!, () &#123;</span><br><span class="line">  element!.mount(<span class="keyword">null</span>, <span class="keyword">null</span>);</span><br><span class="line"> &#125;);</span><br></pre></td></tr></table></figure>

<p>这里的RenderObjectToWidgetElement其实是一个根节点，也就是Element树的根节点，从这个节点开始，我们将一步步地往下构建子Element树.看下RenderObjectToWidgetElement中mount方法的实现</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@override</span></span><br><span class="line"> <span class="keyword">void</span> mount(<span class="built_in">Element?</span> parent, <span class="built_in">Object?</span> newSlot) &#123;</span><br><span class="line">   ...</span><br><span class="line">   <span class="keyword">super</span>.mount(parent, newSlot);</span><br><span class="line">   _rebuild();</span><br><span class="line">   ...</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>可以看到，这里是调用了父类的mount方法，以及调用_rebuild方法。因为这一小节讲得是Widget树的构建。这里重点关注一下super.mount方法，因为RenderObjectToWidgetElement继承RootRenderObjectElement，而RootRenderObjectElement继承RenderObjectElement。所以调用super.mount会调用到RenderObjectElement中的mount方法。如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> mount(<span class="built_in">Element?</span> parent, <span class="built_in">Object?</span> newSlot) &#123;</span><br><span class="line">    <span class="keyword">super</span>.mount(parent, newSlot);</span><br><span class="line">    ...</span><br><span class="line">    _renderObject = widget.createRenderObject(<span class="keyword">this</span>);</span><br><span class="line">    ...</span><br><span class="line">    attachRenderObject(newSlot);</span><br><span class="line">    _dirty = <span class="keyword">false</span>;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<p>这里做了一件事，就是为RenderObjectToWidgetElement绑定一个RenderObject，用于表示这个节点是需要显示在界面。然后执行完mount方法以后，会调用_rebuild()方法。如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> _rebuild() &#123;</span><br><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">     <span class="comment">// 在这个案例中，widget.child 就是我们在runApp方法中传入的MyApp()</span></span><br><span class="line">     _child = updateChild(_child, widget.child, _rootChildSlot);</span><br><span class="line">   &#125; <span class="keyword">catch</span> (exception, stack) &#123;</span><br><span class="line">    ...</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>_rebuild方法中会调用Element这个基类的updateChild方法。如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Element?</span> updateChild(<span class="built_in">Element?</span> child, Widget? newWidget, <span class="built_in">Object?</span> newSlot) &#123;</span><br><span class="line">    <span class="keyword">if</span> (newWidget == <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (child != <span class="keyword">null</span>)</span><br><span class="line">        <span class="comment">//先使子节点child设置为一个不可用状态</span></span><br><span class="line">        deactivateChild(child);</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">final</span> <span class="built_in">Element</span> newChild;</span><br><span class="line">    <span class="keyword">if</span> (child != <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="comment">//如果子节点非空，</span></span><br><span class="line">      <span class="comment">//则再判断是更新，删除子节点或是不对子节点进行操作</span></span><br><span class="line">      <span class="built_in">bool</span> hasSameSuperclass = <span class="keyword">true</span>;</span><br><span class="line">     ...</span><br><span class="line">      <span class="keyword">if</span> (hasSameSuperclass &amp;&amp; child.widget == newWidget) &#123;</span><br><span class="line">        <span class="comment">//widget和久的widget相等的情况下</span></span><br><span class="line">        <span class="comment">//如果只是slot改变，则更新slot。否则widget无需更改</span></span><br><span class="line">        <span class="keyword">if</span> (child.slot != newSlot)</span><br><span class="line">          updateSlotForChild(child, newSlot);</span><br><span class="line">        newChild = child;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (hasSameSuperclass &amp;&amp; Widget.canUpdate(child.widget, newWidget)) &#123;</span><br><span class="line">        <span class="comment">//Widget更新</span></span><br><span class="line">        <span class="keyword">if</span> (child.slot != newSlot)</span><br><span class="line">          updateSlotForChild(child, newSlot);</span><br><span class="line">        child.update(newWidget);</span><br><span class="line">        <span class="keyword">assert</span>(child.widget == newWidget);</span><br><span class="line">        <span class="keyword">assert</span>(() &#123;</span><br><span class="line">          child.owner!._debugElementWasRebuilt(child);</span><br><span class="line">          <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;());</span><br><span class="line">        newChild = child;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">//其他情况，使子节点child设置为一个不可用状态，再重新构建一个子节点</span></span><br><span class="line">        deactivateChild(child);</span><br><span class="line">        <span class="keyword">assert</span>(child._parent == <span class="keyword">null</span>);</span><br><span class="line">        newChild = inflateWidget(newWidget, newSlot);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">//如果子节点为空，则代表是子节点不存在,则为插入一个子节点</span></span><br><span class="line">      newChild = inflateWidget(newWidget, newSlot);</span><br><span class="line">    &#125;</span><br><span class="line">  ...</span><br><span class="line">    <span class="keyword">return</span> newChild;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<p>针对我们runApp方法，其实是一次初次构建。那么这时候RenderObjectElement这时候是没有子节点。也就是说child为空。所以会直接走到newChild = inflateWidget(newWidget, newSlot) 这个方法里面,newWidget就是案例中的MyApp()这个实例 。inflateWidget如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Element</span> inflateWidget(Widget newWidget, <span class="built_in">Object?</span> newSlot) &#123;</span><br><span class="line">  <span class="keyword">assert</span>(newWidget != <span class="keyword">null</span>);</span><br><span class="line">  <span class="keyword">final</span> Key? key = newWidget.key;</span><br><span class="line">  <span class="keyword">if</span> (key <span class="keyword">is</span> GlobalKey) &#123;</span><br><span class="line">    <span class="comment">//Widget的key是否是GlobalKey，</span></span><br><span class="line">    <span class="keyword">final</span> <span class="built_in">Element?</span> newChild = _retakeInactiveElement(key, newWidget);</span><br><span class="line">    <span class="comment">//找到原来的Widget对应的Element,如果在Element树的位置有改变，则更新位置</span></span><br><span class="line">    <span class="keyword">if</span> (newChild != <span class="keyword">null</span>) &#123;</span><br><span class="line">      ...</span><br><span class="line">      newChild._activateWithParent(<span class="keyword">this</span>, newSlot);</span><br><span class="line">      <span class="keyword">final</span> <span class="built_in">Element?</span> updatedChild = updateChild(newChild, newWidget, newSlot);</span><br><span class="line">      <span class="keyword">assert</span>(newChild == updatedChild);</span><br><span class="line">      <span class="keyword">return</span> updatedChild!;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">//调用newWidget，创建一个Element</span></span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">Element</span> newChild = newWidget.createElement();</span><br><span class="line">  ...</span><br><span class="line">  <span class="comment">//调用mount方法，把newChild这个element插入到Element树对应的位置</span></span><br><span class="line">  newChild.mount(<span class="keyword">this</span>, newSlot);</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">return</span> newChild;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到，上面的代码中调用了newWidget.createElement()。也就是MyApp().createElement()的方法。这时候，就终于调用到我们自己写的代码了。从我们的代码可以看出，MyApp继承StatelessWidget。</p>
<p>StatelessWidget的createElement方法会创建一个StatelessElement。所以上方代码中的newChild就是一个StatelessElement类型的Element。而StatelessElement继承ComponentElement。当调用mount方法时，会执行ComponentElement的mount方法。</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> mount(<span class="built_in">Element?</span> parent, <span class="built_in">Object?</span> newSlot) &#123;</span><br><span class="line">  <span class="keyword">super</span>.mount(parent, newSlot);</span><br><span class="line">  ...</span><br><span class="line">  _firstBuild();</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里可以看到，执行了_firstBuild方法。__firstBuild方法最终会调用performRebuild方法。performRebuild如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> performRebuild() &#123;</span><br><span class="line">   ...</span><br><span class="line">      built = build();</span><br><span class="line">   ...</span><br><span class="line">      _child = updateChild(_child, built, slot);</span><br><span class="line">  ...</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<p>这个方法做的事情，就是调用build()方法产生一个Widget。然后再调用updateChild方法，传入Widget，然后去创建，或是更新一个Element。</p>
<p>ComponentElement中的build()方法不做任何事情，交由子类去实现。那么对于刚才的MyApp()这个Widget。对应的就是StatelessElement。StatelessElement的build方法如下。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Widget build() =&gt; widget.build(this);</span><br></pre></td></tr></table></figure>

<p>这个方法就只是简单的返回widget.build(this)。但是，widget.build(this)这个方法是否似曾相识。没错，他其实就是我们平常写StatelessWidget或是State时候的build方法。对于我们的demo，就是MyApp的build方法，如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Widget build(BuildContext context) &#123;</span><br><span class="line">   <span class="keyword">return</span> MaterialApp(</span><br><span class="line">     title: <span class="string">&#x27;Flutter Demo&#x27;</span>,</span><br><span class="line">     theme: ThemeData(),</span><br><span class="line">     home: MyHomePage(title: <span class="string">&#x27;Flutter Demo Home Page&#x27;</span>),</span><br><span class="line">   );</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>留意一下，这里的build方法的参数是BuildContext。结合我们上面的流程可知，BuildContext其实就是Element。理解这一点非常的重要。</p>
<p>结合上面的分析可知，我们平常开发中的BuildContext其实就是父Element节点。通过这个父Element节点，我们可以对节点树进行一些操作,具体看BuildContext的定义。</p>
<p>梳理一下我们刚才的分析，可以大概得出以下这么一个流程。</p>
<ol>
<li>runApp()方法创建RenderObjectToWidgetAdapter，并传入MyApp这个Widget,作为RenderObjectToWidgetAdapter的child。</li>
<li>RenderObjectToWidgetAdapter创建RenderObjectToWidgetElement。</li>
<li>调用RenderObjectToWidgetElement的mount方法</li>
<li>mount方法中调用child或是children的build方法去创建子Widget</li>
<li>调用子Widget的createElement,产生一个子Element.</li>
<li>调用子Element的mount方法</li>
</ol>
<p>然后不断的重复4～6这三个步骤，不断的产生子Widget和子Element，直到Widget树和Element树完全构建为止。</p>
<h4 id="构建流程图"><a href="#构建流程图" class="headerlink" title="构建流程图"></a>构建流程图</h4><p>结合上面的分析以及不同类型的Widget和Element(具体可以看framework.dart实现，这里不做详细分析)。可以得出以下的一个构建流程</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bd58ffb979cc442ea03af1bb1ef154ab~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<h2 id="四-Widget树是怎么更新的？"><a href="#四-Widget树是怎么更新的？" class="headerlink" title="四 Widget树是怎么更新的？"></a>四 Widget树是怎么更新的？</h2><h3 id="State"><a href="#State" class="headerlink" title="State"></a>State</h3><p>我们知道，一个界面不是一成不变的。当Widget和Element树构建完以后，我们需要更新界面。在Flutter开发中，我们一般会通过setState()方法去刷新界面。那调用setState方法发生了什么事情呢？</p>
<p>setState()方法是State里面的一个方法。首先我们先了解一下State是什么。要了解State，要先了解一下StatelessWidget和StatefulWidget。StatelessWidget和StatefulWidget都是继承于Widget。</p>
<p>StatelessWidget代表没有状态的Widget，也就是说这个Widget是不会改变的。StatefulWidget代表的是有状态的Widget，也就是这个Widget是会随着状态的改变。前面提到，Widget终归是只是一个描述类。作用是用来产生节点的。所以我们看下StatelesslElement和StatefulElement这两个类</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StatelessElement</span> <span class="keyword">extends</span> <span class="title">ComponentElement</span> </span>&#123;</span><br><span class="line">  <span class="comment">/// <span class="markdown">Creates an element that uses the given widget as its configuration.</span></span></span><br><span class="line">  StatelessElement(StatelessWidget widget) : <span class="keyword">super</span>(widget);</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  StatelessWidget <span class="keyword">get</span> widget =&gt; <span class="keyword">super</span>.widget <span class="keyword">as</span> StatelessWidget;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build() =&gt; widget.build(<span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="keyword">void</span> update(StatelessWidget newWidget) &#123;</span><br><span class="line">    <span class="keyword">super</span>.update(newWidget);</span><br><span class="line">    _dirty = <span class="keyword">true</span>;</span><br><span class="line">    rebuild();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/// <span class="markdown">An [Element] that uses a [StatefulWidget] as its configuration.</span></span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StatefulElement</span> <span class="keyword">extends</span> <span class="title">ComponentElement</span> </span>&#123;</span><br><span class="line">  <span class="comment">/// <span class="markdown">Creates an element that uses the given widget as its configuration.</span></span></span><br><span class="line">  StatefulElement(StatefulWidget widget)</span><br><span class="line">      : state = widget.createState(),</span><br><span class="line">        <span class="keyword">super</span>(widget) &#123;</span><br><span class="line">   </span><br><span class="line">    state._element = <span class="keyword">this</span>;</span><br><span class="line">    </span><br><span class="line">    state._widget = widget;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build() =&gt; state.build(<span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">final</span> State&lt;StatefulWidget&gt; state;</span><br><span class="line">  ...</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到StatefulElement比StatelessWidget多了一个state。state的代码如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">State</span>&lt;<span class="title">T</span> <span class="keyword">extends</span> <span class="title">StatefulWidget</span>&gt; <span class="title">with</span> <span class="title">Diagnosticable</span> </span>&#123;</span><br><span class="line">  ...</span><br><span class="line">  T <span class="keyword">get</span> widget =&gt; _widget!;</span><br><span class="line">  T? _widget;</span><br><span class="line">  <span class="built_in">bool</span> <span class="keyword">get</span> mounted =&gt; _element != <span class="keyword">null</span>;</span><br><span class="line">  ...</span><br><span class="line">  BuildContext <span class="keyword">get</span> context &#123;</span><br><span class="line">   ...</span><br><span class="line">    <span class="keyword">return</span> _element!;</span><br><span class="line">  &#125;</span><br><span class="line">  StatefulElement? _element;</span><br><span class="line">  ...</span><br><span class="line">   <span class="keyword">void</span> setState(VoidCallback fn) &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">final</span> <span class="built_in">dynamic</span> result = fn() <span class="keyword">as</span> <span class="built_in">dynamic</span>;</span><br><span class="line">   ...</span><br><span class="line">    _element.markNeedsBuild();</span><br><span class="line">  &#125;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到StatefuleElement,StatefuleWidget与State的关系如下</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3bbb765087784e8b901e107db6e88f6c~tplv-k3u1fbpfcp-watermark.image" alt="image.png"><br>widget去创建一个Element,Element的创建时调用Widget的createState方法创建一个State。这个State就代表着Element的状态，它内部持有Widget和Element，可以同时访问到Widget和Element里的内容。</p>
<hr>
<p>从最熟悉的方法，也就是State的setState方法开始讲起。setState方法定义如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> setState(VoidCallback fn) &#123;</span><br><span class="line">   ...</span><br><span class="line">   <span class="keyword">final</span> <span class="built_in">dynamic</span> result = fn() <span class="keyword">as</span> <span class="built_in">dynamic</span>;</span><br><span class="line">  ...</span><br><span class="line">   _element.markNeedsBuild();</span><br><span class="line"> &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>可以看出，这个方法先是我们调用了传入的方法，执行方法后，就调用了State中的_element的markNeedsBuild()方法，markNeedsBuild()方法是将一个Element标记为需要更新。方法的定义如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> markNeedsBuild() &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">if</span> (!_active) <span class="keyword">return</span>;</span><br><span class="line">    ...</span><br><span class="line">    _dirty = <span class="keyword">true</span>;</span><br><span class="line">    owner.scheduleBuildFor(<span class="keyword">this</span>); <span class="comment">//加入build owner的rebuild计划</span></span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<p>markNeedsBuild方法首先判断一个Elemnt是否是_active，如果不是，则不做任何处理，因为一个非活跃的状态不会显示在界面上，所以不需做处理。然后将其_dirty值设置为true,标记这个Element 需要更新。然后调用BuildOwner的scheduleBuildFor方法,并传入需要更新的Element。BuildOwner的scheduleBuildFor定义如下</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">void scheduleBuildFor(Element element) &#123;</span><br><span class="line">   ...</span><br><span class="line">   if (!_scheduledFlushDirtyElements &amp;&amp; onBuildScheduled != null) &#123;</span><br><span class="line">     _scheduledFlushDirtyElements = true;</span><br><span class="line">     onBuildScheduled(); // 通知Engine 在下一帧需要做更新操作；</span><br><span class="line">   &#125;</span><br><span class="line">   _dirtyElements.add(element);</span><br><span class="line">   ...</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>方法里的_scheduledFlushDirtyElements表明是否是在更新过程中，可以看到，这里先判断_scheduledFlushDirtyElements是否为true，就是不在更新过程中，则调用onBuildScheduled通知框架需要进行更新树。 然后将刚才传入的element添加到BuildOwner的_dirtyElements中。这个_dirtyElements是一个列表，存储着需要所有更新的Elemnt。</p>
<p>当框架收到需要更新树的信息后，就会调用BuildOwner的buildScope()方法，前面在构建过程中提到过这个buildScope方法，但是没有细说，在这里我们看一下buildScope方法的实现</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> buildScope(<span class="built_in">Element</span> context, [VoidCallback callback]) &#123;</span><br><span class="line">    <span class="keyword">if</span> (callback == <span class="keyword">null</span> &amp;&amp; _dirtyElements.isEmpty) <span class="keyword">return</span>;</span><br><span class="line">    ...</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      _scheduledFlushDirtyElements = <span class="keyword">true</span>;</span><br><span class="line">      <span class="keyword">if</span> (callback != <span class="keyword">null</span>) &#123;</span><br><span class="line">       ...</span><br><span class="line">         callback(); <span class="comment">//callback先执行</span></span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">while</span> (index &lt; dirtyCount) &#123;</span><br><span class="line">        ...</span><br><span class="line">         _dirtyElements[index].rebuild(); <span class="comment">//进行rebuild</span></span><br><span class="line">        ...</span><br><span class="line">      &#125;</span><br><span class="line">          _dirtyElements.clear(); </span><br><span class="line">  &#125;</span><br><span class="line">复制代码</span><br></pre></td></tr></table></figure>

<p>buildScope()方法中先是执行了callback方法，对于首次构建的流程，这个callback方法就是调用RootRenderObjectElement,也就是根Elemnt的mount方法进行构建。</p>
<p>然后这个方法主要就做了一件事，就是从_dirtyElements列表中取出每一个需要更新的Element，然后对所有Elemnt调用rebuild()方法,再清空_dirtyElements列表，标志着这一轮更新完成。</p>
<p>Element中的rebuild方法会调用performRebuild方法。performRebuild在Element中没有具体的实现。对于不同的Element子类，有着不一样的额外的实现。</p>
<p>首先对于RenderObjctElemnt及其子类，performRebuild只会调用updateRenderObject去更新RenderObjct对象，不会涉及任何的子节点的更新.那这里就有疑问了，该怎么对有子节点的RenderObjctElemnt进行更新子树呢？这里先留一下小疑问，等讲完ComponetElemnt然后再解答。</p>
<p>对于ComponetElemnt及其子类,调用performRebuild方法会调用updateChild方法。前面在构建过程中，就贴出了updateChild这两个方法，这里就不再重复去贴代码。在这里重点说一下updateChild这个方法</p>
<h3 id="updateChild"><a href="#updateChild" class="headerlink" title="updateChild"></a>updateChild</h3><p>updateChild中传入了(Element child, Widget newWidget, dynamic newSlot) 三个参数。child代表的是该ComponetElemnt的子节点，newWidget是子节点对应的Widget，newSlot是子节点的位置信息</p>
<p>在执行updateChild过程中根据传入的参数做了以下的一些处理。</p>
<ol>
<li>如果new widget为空，但是element不为空（也就是原有的widget被删除了）。首先deactivateChild（child）,如果child不为空，则解绑child的renderobjce，并添加到build owner中的_inactiveElements列表中，并返回函数。deactivateChild(child会把child添加到BuildOwner_inactiveElements中)</li>
<li>如果new widget非空，child不为空<ol>
<li>如果传入的widget和原来的widget是同一个，但是slot不一样，则调用updateSlotForChild更新位置</li>
<li> 如果不是同一个widget，则判断widget的canUpdate是否是true，如果是true的话（代表element的可以重用），先判断slot是否和原来的slot相等，不相等，则调用updateSlotForChild更新位置。然后调用elemnt的update方法进行更新</li>
<li> 如果不符合上面2.1,2.2的情况(则代表element不可重用，newWidget需要用例外一个新的element)，则调用deactivateChild(child)方法，并调用inflateWidget(newWidget, newSlot)产生新的child</li>
</ol>
</li>
<li>newWidget为空，child为空。则表明该Wiget还不存在Element，则调用inflateWidget去创建一个新的Element。</li>
</ol>
<p>上面的1，2，3分别代表着删除，修改，增加 Elemnt子节点的三种情况。当对一个element(ComponetElemnt及其子类的实例)调用markNeedsBuild方法的时候，会调用到updateChild方法去更新该element。</p>
<p>对于步骤1，当一个Widget不再使用的时候，会调用deactivateChild方法，这个方法会把对应的Element放入到BuildlOwner的_inactiveElements列表中，如果Element被再次使用到(如使用了GlobalKey)，就会从_inactiveElements列表中移除。如果没有被再次用到，再一次更新树的时候就会被销毁。</p>
<p>对于步骤2.2，elemnt的update方法只会简单的设置widget。具体的实现由各个子类实现。这一步可以buid方法可以沿下更新树</p>
<p>如StatelessElement会调用rebuild方法，RenderObjectElemnt会更新renderobjct。像SingleChildRenderObject和MutilChildRederObjectElemnt等有子elemnt的还会调用updateChild方法(MutilChildRederObjectElemnt 是调用updateChildren,但是updateChildren是对updateChild的一个包装，对传入的列表逐个调用updateChild)更新子elemnt,</p>
<p>在说RenderObjctElemnt的performRebuild时候，留了一个小疑问：如何更新有子节点的RenderObjcetElement的子树。</p>
<p>其实在开发过程中，我们是不会直接调用RenderObjcetElement的markNeedsBuild方法的。就拿Stack这个Wigdet来说。Stack继承自MultiChildRenderObjectWidget。MultiChildRenderObjectWidget对应的是MutilChildRederObjectElemnt。</p>
<p>但会发现，Stack是没有setState方法的，也就说不会直接调用对应Element的markNeedsBuild方法。Stack一般是被ComponetWidget及其子类对应的Widget(如StatefuleWidget)所包裹着，也就是Stack的一般都有一个父Widget包裹着。更新的时候，就会调用父Widget对应Element的updateChild更新子树。</p>
<p>假设你要在更新流程中，保留Stack，只删除Stack里的一个子节点，会走到父Widget对应的Element的updateChild方法中，也就是上方的步骤2.2。</p>
<p>步骤2.2中调用了child.update(newWidget)。也就是Stack对应的Element的update方法</p>
<p>Stack对应的Element是MutilChildRederObjectElement，update方法如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="keyword">void</span> update(MultiChildRenderObjectWidget newWidget) &#123;</span><br><span class="line">    <span class="keyword">super</span>.update(newWidget);</span><br><span class="line">	...</span><br><span class="line">    _children = updateChildren(_children, widget.children,</span><br><span class="line">        forgottenChildren: _forgottenChildren);</span><br><span class="line">    _forgottenChildren.clear();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到，update方法调用了updateChildren方法，updateChildren这个方法会遍历子节点，对每一个子节点调用updateChild方法，从而完成RenderObjcetElement子树的更新。</p>
<p>验证这一个流程也很简单，在MutilChildRederObjectElement那里的update方法打个断点即可，如下。观察左方的调用栈即可得到具体的流程</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e0430efdd94841cb8990c84604d3ce7f~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9368448436404d8db6f2609bbde7b4b9~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<p>更新流程图</p>
<p>综合上面的流程可以看出，调用State的setState方法，其实就是标记State对应的Element需要更新，通过调用markNeedsRebuild方法标记Element需要更新并通知到Flutter框架需要更细。当框架更新该Element的时候，会调用updateChild方法向下递归更新子树，直到叶子节点为止</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b6e8ad281ae943de8892b37c7b3c7510~tplv-k3u1fbpfcp-watermark.image" alt="widget树的更新流程.jpg"></p>
<h2 id="五-Widget-Eleemtn-RenderObject树的补充"><a href="#五-Widget-Eleemtn-RenderObject树的补充" class="headerlink" title="五 Widget,Eleemtn,RenderObject树的补充"></a>五 Widget,Eleemtn,RenderObject树的补充</h2><p>上方的三，四小节标题虽然是Widget的构建和更新。但是其实Widget的构建和更新其实都是依赖Element的。在实际内容中其实是Widget和Element树的更新。但是对于RenderObject树的构建没有太多的描述。</p>
<p>如果对上方Element的构建流程理解以后，再结合以下的代码，你就知道RenderObjct树是怎么构建的。</p>
<h3 id="RenderObject树的构建"><a href="#RenderObject树的构建" class="headerlink" title="RenderObject树的构建"></a>RenderObject树的构建</h3><p>首先看下RenderObjectElement的mount方法，如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> mount(<span class="built_in">Element?</span> parent, <span class="built_in">Object?</span> newSlot) &#123;</span><br><span class="line">    <span class="keyword">super</span>.mount(parent, newSlot);</span><br><span class="line">   ...</span><br><span class="line">     <span class="comment">//创建RenderObjct</span></span><br><span class="line">    _renderObject = widget.createRenderObject(<span class="keyword">this</span>);</span><br><span class="line">   ...</span><br><span class="line">    attachRenderObject(newSlot);</span><br><span class="line">   ...</span><br><span class="line">  &#125;</span><br><span class="line">   </span><br><span class="line">...</span><br><span class="line"><span class="keyword">void</span> attachRenderObject(<span class="built_in">Object?</span> newSlot) &#123;</span><br><span class="line">    <span class="keyword">assert</span>(_ancestorRenderObjectElement == <span class="keyword">null</span>);</span><br><span class="line">    <span class="comment">//找到父RenderObjct节点，并在父RenderObjct节点的字节点中加入该RenderObject节点</span></span><br><span class="line">    _slot = newSlot;</span><br><span class="line">    _ancestorRenderObjectElement = _findAncestorRenderObjectElement();</span><br><span class="line">    _ancestorRenderObjectElement?.insertRenderObjectChild(</span><br><span class="line">        renderObject, newSlot);</span><br><span class="line">    <span class="keyword">final</span> ParentDataElement&lt;ParentData&gt;? parentDataElement =</span><br><span class="line">        _findAncestorParentDataElement();</span><br><span class="line">    <span class="keyword">if</span> (parentDataElement != <span class="keyword">null</span>) _updateParentData(parentDataElement.widget);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<p>首先在Element的构建过程中，去创建RenderObject。然后在通过Element树找到父节点，并通过_ancestorRenderObjectElement指向父节点。这样在Element树构建的过程中，RenderObject树也会不断的构建。</p>
<h3 id="RenderObject树更新"><a href="#RenderObject树更新" class="headerlink" title="RenderObject树更新"></a>RenderObject树更新</h3><p>看下RenderObjectElement的update方法，如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> update(<span class="keyword">covariant</span> RenderObjectWidget newWidget) &#123;</span><br><span class="line">  <span class="keyword">super</span>.update(newWidget);</span><br><span class="line">  ...</span><br><span class="line">  widget.updateRenderObject(<span class="keyword">this</span>, renderObject);</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>RenderObject树的更新也是依赖于Element树的更新。当Element更新的时候，会调用RenderObjectElement的update方法，然后调用对应Widget的updateRenderObject方法。</p>
<p>对于开发者而言，对RenderObject的操作基本都是通过Widget去实现，基本屏蔽了Element的实现。</p>
<h2 id="六-拓展"><a href="#六-拓展" class="headerlink" title="六 拓展"></a>六 拓展</h2><h3 id="Flutter中的Key"><a href="#Flutter中的Key" class="headerlink" title="Flutter中的Key"></a>Flutter中的Key</h3><p>Flutter中有各种各样的Key，作用大致都是用来标志一个对象的。集成体系大致如下</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/53c905ca43b342c2a480e26ce2d04f2a~tplv-k3u1fbpfcp-watermark.image" alt="image.png"></p>
<p>大家可以直接看下注释就可以知道不同的Key是用来做什么的。这里想重点说一下GlobalKey。很多人用来做跨Widget访问状态使用。</p>
<p>先看下它的定义</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">GlobalKey</span>&lt;<span class="title">T</span> <span class="keyword">extends</span> <span class="title">State</span>&lt;<span class="title">StatefulWidget</span>&gt;&gt; <span class="keyword">extends</span> <span class="title">Key</span> </span>&#123;</span><br><span class="line"> ...</span><br><span class="line">  <span class="keyword">factory</span> GlobalKey(&#123;<span class="built_in">String?</span> debugLabel&#125;) =&gt; LabeledGlobalKey&lt;T&gt;(debugLabel);</span><br><span class="line"> ...</span><br><span class="line">  <span class="keyword">const</span> GlobalKey.constructor() : <span class="keyword">super</span>.empty();</span><br><span class="line"></span><br><span class="line">  <span class="built_in">Element?</span> <span class="keyword">get</span> _currentElement =&gt;</span><br><span class="line">      WidgetsBinding.instance!.buildOwner!._globalKeyRegistry[<span class="keyword">this</span>];</span><br><span class="line"> ...</span><br><span class="line">  BuildContext? <span class="keyword">get</span> currentContext =&gt; _currentElement;</span><br><span class="line"> ...</span><br><span class="line">  Widget? <span class="keyword">get</span> currentWidget =&gt; _currentElement?.widget;</span><br><span class="line"></span><br><span class="line"> ...</span><br><span class="line">  T? <span class="keyword">get</span> currentState &#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="built_in">Element?</span> element = _currentElement;</span><br><span class="line">    <span class="keyword">if</span> (element <span class="keyword">is</span> StatefulElement) &#123;</span><br><span class="line">      <span class="keyword">final</span> StatefulElement statefulElement = element;</span><br><span class="line">      <span class="keyword">final</span> State state = statefulElement.state;</span><br><span class="line">      <span class="keyword">if</span> (state <span class="keyword">is</span> T) <span class="keyword">return</span> state;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到，GlobalKey是用于标记一个State的。同时它有currentContext，currentWidget,和currentState。允许你在Widget树上的任何一个节点获取到其持有的Widget,State,Context。那是如何做到的呢？</p>
<p>当我在Widget中声明一个GlobalKey时，随着Widget树的构建，会调用Element树的mount方法</p>
<p>看下Element的mount方法,如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> mount(<span class="built_in">Element?</span> parent, <span class="built_in">Object?</span> newSlot) &#123;</span><br><span class="line">  ...</span><br><span class="line">   _parent = parent;</span><br><span class="line">   _slot = newSlot;</span><br><span class="line">   _lifecycleState = _ElementLifecycle.active;</span><br><span class="line">   _depth = _parent != <span class="keyword">null</span> ? _parent!.depth + <span class="number">1</span> : <span class="number">1</span>;</span><br><span class="line">   <span class="keyword">if</span> (parent != <span class="keyword">null</span>) &#123;</span><br><span class="line">    ...</span><br><span class="line">     _owner = parent.owner;</span><br><span class="line">   &#125;</span><br><span class="line">    ...</span><br><span class="line">   <span class="keyword">final</span> Key? key = widget.key;</span><br><span class="line">  <span class="comment">//是否是GlobalKey，如果是，向BuildOwner中注册一个GlobalKey</span></span><br><span class="line">   <span class="keyword">if</span> (key <span class="keyword">is</span> GlobalKey) &#123;</span><br><span class="line">     owner!._registerGlobalKey(key, <span class="keyword">this</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   _updateInheritance();</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>可以看到，如果一个Widget声明了一个GlobalKey，那么对应的Element就会在BuildOwner中调用_registerGlobalKey注册。BuildOwner的_registerGlobalKey如下</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> _registerGlobalKey(GlobalKey key, <span class="built_in">Element</span> element) &#123;</span><br><span class="line">    ...</span><br><span class="line">    _globalKeyRegistry[key] = element;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<p>_globalKeyRegistry是BuildOwner的一个Map,用于存储所有的GlobalKey和其对应的Element.</p>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="built_in">Map</span>&lt;GlobalKey, <span class="built_in">Element</span>&gt; _globalKeyRegistry = &lt;GlobalKey, <span class="built_in">Element</span>&gt;&#123;&#125;;</span><br></pre></td></tr></table></figure>

<p>当调用_registerGlobalKey的时候，实际上就是把GlobalKey和Element绑定在一起，放入到BuildOwner的一个Map当中。</p>
<p>前面提到过BuildOwner，BuildOwner负责Widegt树的构建和更新的，一般在Flutter的声明周期中，一般只会有一个BuildOwner实例(这一块在讨论Flutter中的runApp流程的时候再讨论)</p>
<p>当我们去使用GlobalKey去拿Widget,Context,State的时候，其实就是以这个GlobalKey为键，去BuildOwner中取出对应的Element。再通过Element 获取到对应的Widget,State,Context。</p>
<p>就是那么的简单！！！</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>其实理解Widget,Element,RenderObject树的概念不是必要的，但是很重要。对于后续理解app的流程，手势传递等，以及优化等分析都是必不可少的部分。</p>
<p>这是一篇自己写过的文章，<a target="_blank" rel="noopener" href="https://juejin.cn/post/6952076273891934221">原文地址</a>。为什么要重写一遍呢？一是原来的文章流程图上格式有些混乱，之前的流程图也丢失了，所以重新整理了一遍。二是之前的源码偏多，最近也正好重新看了下源码，于是换个思路再写一篇那样。</p>

    </div>

    
    
    

      <footer class="post-footer">

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2022/01/07/iOS/1/ios1/" rel="prev" title="理解iOS app的编译，运行过程">
      <i class="fa fa-chevron-left"></i> 理解iOS app的编译，运行过程
    </a></div>
      <div class="post-nav-item">
    <a href="/2022/01/08/Flutter/2/flutter2/" rel="next" title="Flutter源码阅读(2)-Flutter App的启动流程更新流程">
      Flutter源码阅读(2)-Flutter App的启动流程更新流程 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          Table of Contents
        </li>
        <li class="sidebar-nav-overview">
          Overview
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B8%80-%E5%89%8D%E8%A8%80"><span class="nav-number">1.</span> <span class="nav-text">一 前言</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%AF%B4%E6%98%8E"><span class="nav-number">1.1.</span> <span class="nav-text">说明</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%97%AE%E9%A2%98"><span class="nav-number">1.2.</span> <span class="nav-text">问题</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BA%8C-Widget-Element-RenderObjct"><span class="nav-number">2.</span> <span class="nav-text">二 Widget,Element,RenderObjct</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Widget-Element-RenderObject%E8%AF%B4%E6%98%8E"><span class="nav-number">2.1.</span> <span class="nav-text">Widget,Element,RenderObject说明</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B8%89-Widget%E6%A0%91%E6%9E%84%E5%BB%BA"><span class="nav-number">3.</span> <span class="nav-text">三 Widget树构建</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Widget%E6%A0%91%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-number">3.1.</span> <span class="nav-text">Widget树是什么？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Widget%E6%A0%91%E6%80%8E%E4%B9%88%E6%9E%84%E5%BB%BA%EF%BC%9F"><span class="nav-number">3.2.</span> <span class="nav-text">Widget树怎么构建？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%9E%84%E5%BB%BA%E7%AE%A1%E7%90%86%E8%80%85BuilderOwner"><span class="nav-number">3.2.1.</span> <span class="nav-text">构建管理者BuilderOwner</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%9E%84%E5%BB%BA%E6%B5%81%E7%A8%8B"><span class="nav-number">3.2.2.</span> <span class="nav-text">构建流程</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%9E%84%E5%BB%BA%E6%B5%81%E7%A8%8B%E5%9B%BE"><span class="nav-number">3.2.3.</span> <span class="nav-text">构建流程图</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%9B%9B-Widget%E6%A0%91%E6%98%AF%E6%80%8E%E4%B9%88%E6%9B%B4%E6%96%B0%E7%9A%84%EF%BC%9F"><span class="nav-number">4.</span> <span class="nav-text">四 Widget树是怎么更新的？</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#State"><span class="nav-number">4.1.</span> <span class="nav-text">State</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#updateChild"><span class="nav-number">4.2.</span> <span class="nav-text">updateChild</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BA%94-Widget-Eleemtn-RenderObject%E6%A0%91%E7%9A%84%E8%A1%A5%E5%85%85"><span class="nav-number">5.</span> <span class="nav-text">五 Widget,Eleemtn,RenderObject树的补充</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#RenderObject%E6%A0%91%E7%9A%84%E6%9E%84%E5%BB%BA"><span class="nav-number">5.1.</span> <span class="nav-text">RenderObject树的构建</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#RenderObject%E6%A0%91%E6%9B%B4%E6%96%B0"><span class="nav-number">5.2.</span> <span class="nav-text">RenderObject树更新</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%85%AD-%E6%8B%93%E5%B1%95"><span class="nav-number">6.</span> <span class="nav-text">六 拓展</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Flutter%E4%B8%AD%E7%9A%84Key"><span class="nav-number">6.1.</span> <span class="nav-text">Flutter中的Key</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%80%BB%E7%BB%93"><span class="nav-number">7.</span> <span class="nav-text">总结</span></a></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="samstring"
      src="https://p9-passport.byteacctimg.com/img/user-avatar/b02abbd29495eadaaf476f3b5cb129bf~300x300.image">
  <p class="site-author-name" itemprop="name">samstring</p>
  <div class="site-description" itemprop="description">问号青年</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives">
          <span class="site-state-item-count">6</span>
          <span class="site-state-item-name">posts</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
        <span class="site-state-item-count">2</span>
        <span class="site-state-item-name">categories</span></a>
      </div>
  </nav>
</div>


  <div class="links-of-blogroll motion-element">
    <div class="links-of-blogroll-title"><i class="fa fa-link fa-fw"></i>
      Links
    </div>
    <ul class="links-of-blogroll-list">
        <li class="links-of-blogroll-item">
          <a href="https://juejin.cn/user/3957854362672798" title="https:&#x2F;&#x2F;juejin.cn&#x2F;user&#x2F;3957854362672798" rel="noopener" target="_blank">掘金</a>
        </li>
    </ul>
  </div>

      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">samstring</span>
</div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  

</body>
</html>
